package mysql

import (
	m "github.com/go-sql-driver/mysql"
	aes256 "gluenet/pkg/aes"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"strings"
	"sync"
	"time"
)

var db *gorm.DB
var once sync.Once

type Conf struct {
	IsEncrypted     bool          `mapstructure:"isEncrypted"`
	LogMode         bool          `mapstructure:"logMode"` // Whether to enable MySQL log printing：ture/false
	User            string        `mapstructure:"username"`
	Passwd          string        `mapstructure:"passwd"`
	IP              string        `mapstructure:"ip"`
	Port            string        `mapstructure:"port"`
	DBName          string        `mapstructure:"dbName"`
	ConnTimeout     string        `mapstructure:"connTimeout"`
	ConnMaxLifeTime time.Duration `mapstructure:"connMaxLifeTime"`
	MaxConn         int           `mapstructure:"maxConn"`
	MaxIdleConn     int           `mapstructure:"maxIdleConn"`
}

func (c Conf) ToConfig() (*m.Config, error) {
	cfg := m.Config{
		Addr:                 strings.Join([]string{c.IP, c.Port}, ":"),
		DBName:               c.DBName,
		Loc:                  time.Local,
		Net:                  "tcp",
		Params:               map[string]string{"parseTime": "true", "timeout": c.ConnTimeout, "charset": "utf8mb4"},
		AllowNativePasswords: true,
		MultiStatements:      true, // Open batch
	}

	// User name and password can be encrypted
	if c.IsEncrypted {
		user, err := aes256.Decrypt(c.User, aes256.DefaultKey)
		if err != nil {
			return nil, err
		}
		cfg.User = user

		pwd, err := aes256.Decrypt(c.Passwd, aes256.DefaultKey)
		if err != nil {
			return nil, err
		}
		cfg.Passwd = pwd
	} else {
		cfg.User = c.User
		cfg.Passwd = c.Passwd
	}

	return &cfg, nil
}

func GetDB() *gorm.DB {
	return db
}

func InitMysqlDB(config Conf) {
	once.Do(func() {

		conf, err := config.ToConfig()
		if err != nil {
			panic(err)
		}

		database, err := gorm.Open(mysql.Open(conf.FormatDSN()), &gorm.Config{
			NamingStrategy: schema.NamingStrategy{
				SingularTable: true,
			},
			Logger: logger.Default.LogMode(logger.Info),
		})
		if err != nil {
			panic(err)
		}
		sqlDB, _ := database.DB()
		sqlDB.SetConnMaxLifetime(config.ConnMaxLifeTime)
		sqlDB.SetMaxOpenConns(config.MaxConn)
		sqlDB.SetMaxIdleConns(config.MaxIdleConn)

		db = database
	})
}
